home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet bezpieczenstwa / mini Pentoo LiveCD 2006.1 / mpentoo-2006.1.iso / livecd.squashfs / usr / bin / equery < prev    next >
Text File  |  2006-05-08  |  49KB  |  1,676 lines

  1. #!/usr/bin/python
  2. #
  3. # Copyright 2003-2004 Karl Trygve Kalleberg
  4. # Copyright 2003-2004 Gentoo Technologies, Inc.
  5. # Distributed under the terms of the GNU General Public License v2
  6. #
  7. # $Header$
  8. # Author: Karl Trygve Kalleberg <karltk@gentoo.org>
  9.  
  10. __author__ = "Karl Trygve Kalleberg"
  11. __email__ = "karltk@gentoo.org"
  12. __version__ = "0.1.4"
  13. __productname__ = "equery"
  14. __description__ = "Gentoo Package Query Tool"
  15.  
  16. import os
  17. import re
  18. import sys
  19. import time
  20. import string
  21. import types
  22.  
  23. # portage (output module) and gentoolkit need special path modifications
  24. sys.path.insert(0, "/usr/lib/portage/pym")
  25. sys.path.insert(0, "/usr/lib/gentoolkit/pym")
  26.  
  27. import gentoolkit
  28. import portage
  29. import portage_checksum
  30. from portage_util import unique_array
  31.  
  32. import gentoolkit.pprinter as pp
  33. from gentoolkit.pprinter import print_info, print_error, print_warn, die
  34.  
  35. # Auxiliary functions
  36.  
  37. def fileAsStr(name, fdesc, showType=0, showMD5=0, showTimestamp=0):
  38.     """
  39.     Return file in fdesc as a filename
  40.     @param name: 
  41.     @param fdesc:
  42.     @param showType:
  43.     @param showMD5:
  44.     @param showTimestamp:
  45.     @rtype: string
  46.     """
  47.     type = ""; fname = ""; stamp = ""; md5sum = ""
  48.  
  49.     if fdesc[0] == 'obj':
  50.         type = "file"
  51.         fname = name
  52.         stamp = timestampAsStr(int(fdesc[1]))
  53.         md5sum = fdesc[2]
  54.     elif fdesc[0] == "dir":
  55.         type = "dir"
  56.         fname = pp.path(name)
  57.     elif fdesc[0] == "sym":
  58.         type = "symlink"
  59.         stamp = timestampAsStr(int(fdesc[1].replace(")","")))
  60.         tgt = fdesc[2].split()[0]
  61.         if Config["piping"]:
  62.             fname = name
  63.         else:
  64.             fname = pp.path_symlink(name + " -> " + tgt)
  65.     elif fdesc[0] == "fif":
  66.         type = "fifo"
  67.         fname = name
  68.     else:
  69.         raise Exception(fdesc[1] + " has unknown type: " + fdesc[0])
  70.  
  71.     s = ""
  72.     if showType:
  73.         s += "%6s " % type
  74.     s += fname
  75.     if showTimestamp:
  76.         s += " " + stamp + " "
  77.     if showMD5:
  78.         s += " " + md5sum + " "
  79.     return s
  80.  
  81. def timestampAsStr(timestamp):
  82.     return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp))
  83.  
  84.     
  85. class Command:
  86.     """
  87.     Abstract root class for all equery commands
  88.     """
  89.     def __init__(self):
  90.         pass
  91.     def shortHelp(self):
  92.         """Return a help formatted to fit a single line, approx 70 characters.
  93.         Must be overridden in the subclass."""
  94.         return " - not implemented yet"
  95.     def longHelp(self):
  96.         """Return full, multiline, color-formatted help.
  97.         Must be overridden in the subclass."""
  98.         return "help for syntax and options"
  99.     def perform(self, args):
  100.         """Stub code for performing the command.
  101.         Must be overridden in the subclass"""
  102.         pass
  103.     def parseArgs(self, args):
  104.         """Stub code for parsing command line arguments for this command.
  105.         Must be overridden in the subclass."""
  106.         pass
  107.  
  108.     
  109. class CmdListFiles(Command):
  110.     """List files owned by a particular package"""
  111.     def __init__(self):
  112.         self.default_options = {
  113.             "showType": 0,
  114.             "showTimestamp": 0,
  115.             "showMD5": 0,
  116.             "filter": None
  117.             }
  118.  
  119.     def parseArgs(self,args):
  120.         query = ""
  121.         need_help = 0
  122.         opts = self.default_options
  123.         for x in args:
  124.             if x in ["-h", "--help"]:
  125.                 need_help = 1
  126.             elif x in ["--md5sum"]:
  127.                 opts["showMD5"] = 1
  128.             elif x in ["--timestamp"]:
  129.                 opts["showTimestamp"] = 1
  130.             elif x in ["--type"]:
  131.                 opts["showType"] = 1
  132.             elif x[:9] == "--filter=":
  133.                 opts["filter"] = string.split(x[9:],',')
  134.             elif x[0] == "/":
  135.                 die(2, "The query '" + pp.pkgquery(x) + "' does not appear to be a valid package specification")
  136.             else:
  137.                 query = x
  138.  
  139.         if need_help or query == "":
  140.             print_info(0, self.longHelp())
  141.             sys.exit(-1)
  142.             
  143.         return (query, opts)
  144.  
  145.     def filterContents(self, cnt, filter):
  146.         if filter in [None,[]]:
  147.             return cnt
  148.         
  149.         mycnt = {}
  150.         
  151.         for mytype in filter:
  152.             # Filter elements by type (as recorded in CONTENTS).
  153.             if mytype in ["dir","obj","sym","dev","fif"]:
  154.                 for mykey in cnt.keys():
  155.                     if cnt[mykey][0] == mytype:
  156.                         mycnt[mykey] = cnt[mykey]
  157.         
  158.         if "cmd" in filter:
  159.             # List files that are in $PATH.
  160.             userpath = map(os.path.normpath,os.environ["PATH"].split(os.pathsep))
  161.             for mykey in cnt.keys():
  162.                 if cnt[mykey][0] in ['obj','sym'] \
  163.                    and os.path.dirname(mykey) in userpath:
  164.                     mycnt[mykey] = cnt[mykey]
  165.         
  166.         if "path" in filter:
  167.             # List only dirs where some files where actually installed,
  168.             # and also skip their subdirs.
  169.             mykeys = cnt.keys()
  170.             mykeys.sort()
  171.             while len(mykeys):
  172.                 mykey = mykeys.pop(0)
  173.                 if cnt[mykey][0] == 'dir':
  174.                     i = 0
  175.                     while i < len(mykeys) :
  176.                         if cnt[mykeys[i]][0] != "dir" \
  177.                            and os.path.dirname(mykeys[i]) == mykey:
  178.                             mycnt[mykey] = cnt[mykey]
  179.                             break
  180.                         i += 1
  181.                     if i < len(mykeys):
  182.                         while len(mykeys) \
  183.                               and len(mykey+"/") < len(mykeys[0]) \
  184.                               and mykey+"/" == mykeys[0][:len(mykey)+1]:
  185.                             mykeys.pop(0)
  186.         
  187.         if "conf" in filter:
  188.             # List configuration files.
  189.             conf_path = gentoolkit.settings["CONFIG_PROTECT"].split()
  190.             conf_mask_path = gentoolkit.settings["CONFIG_PROTECT_MASK"].split()
  191.             conf_path = map(os.path.normpath, conf_path)
  192.             conf_mask_path = map(os.path.normpath, conf_mask_path)
  193.             for mykey in cnt.keys():
  194.                 is_conffile = False
  195.                 if cnt[mykey][0] == 'obj':
  196.                     for conf_dir in conf_path:
  197.                         if conf_dir+"/" == mykey[:len(conf_dir)+1]:
  198.                             is_conffile = True
  199.                             for conf_mask_dir in conf_mask_path:
  200.                                 if conf_mask_dir+"/" == mykey[:len(conf_mask_dir)+1]:
  201.                                     is_conffile = False
  202.                                     break
  203.                             break
  204.                     if is_conffile:
  205.                         mycnt[mykey] = cnt[mykey]
  206.               
  207.         
  208.         for mydoctype in ["doc","man","info"]:
  209.             # List only files from /usr/share/{doc,man,info}
  210.             mydocpath = "/usr/share/"+mydoctype+"/"
  211.             if mydoctype in filter:
  212.                 for mykey in cnt.keys():
  213.                     if cnt[mykey][0] == 'obj' \
  214.                        and mykey[:len(mydocpath)] == mydocpath :
  215.                         mycnt[mykey] = cnt[mykey]
  216.         
  217.         return mycnt
  218.  
  219.     def perform(self, args):
  220.  
  221.         (query, opts) = self.parseArgs(args)
  222.  
  223.         if not Config["piping"] and Config["verbosityLevel"] >= 3:
  224.             print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]")
  225.             
  226.         pkgs = gentoolkit.find_installed_packages(query, True)
  227.         for x in pkgs:
  228.             if not x.is_installed():
  229.                 continue
  230.         
  231.             if not Config["piping"] and Config["verbosityLevel"] >= 3:
  232.                 print_info(1, pp.section("* ") + "Contents of " + pp.cpv(x.get_cpv()) + ":")
  233.  
  234.             cnt = self.filterContents(x.get_contents(),opts["filter"])
  235.  
  236.             filenames = cnt.keys()
  237.             filenames.sort()
  238.             
  239.             for name in filenames:
  240.                 print_info(0, fileAsStr(name,
  241.                                     cnt[name],
  242.                                     showType=opts["showType"],
  243.                                     showTimestamp=opts["showTimestamp"],
  244.                                     showMD5=opts["showMD5"]))
  245.         
  246.     def longHelp(self):
  247.         return "List files owned by a particular package\n" + \
  248.                "\n" + \
  249.                "Syntax:\n" + \
  250.                "  " + pp.command("files") + pp.localoption(" <local-opts> ") + pp.pkgquery("<cat/>packagename<-version>") + "\n" + \
  251.                "\n" + \
  252.                "Note: category and version parts are optional. \n" + \
  253.                "\n" + \
  254.                pp.localoption("<local-opts>") + " is either of: \n" + \
  255.                "  " + pp.localoption("--timestamp") + "      - append timestamp\n" + \
  256.                "  " + pp.localoption("--md5sum") + "         - append md5sum\n" + \
  257.                "  " + pp.localoption("--type") + "           - prepend file type\n" + \
  258.                "  " + pp.localoption("--filter=<rules>") + " - filter output\n" + \
  259.                "  " + pp.localoption("<rules>") + " is a comma separated list of elements you want to see:\n" + \
  260.                "  " + "  " + pp.localoption("dir") + \
  261.                       ", " + pp.localoption("obj") + \
  262.                       ", " + pp.localoption("sym") + \
  263.                       ", " + pp.localoption("dev") + \
  264.                       ", " + pp.localoption("fifo") + \
  265.                       ", " + pp.localoption("path") + \
  266.                       ", " + pp.localoption("conf") + \
  267.                       ", " + pp.localoption("cmd") + \
  268.                       ", " + pp.localoption("doc") + \
  269.                       ", " + pp.localoption("man") + \
  270.                       ", " + pp.localoption("info")
  271.  
  272.     def shortHelp(self):
  273.         return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - list files owned by " + pp.pkgquery("pkgspec")
  274.  
  275.     
  276. class CmdListBelongs(Command):
  277.     """List all packages owning a particular file"""
  278.     def __init__(self):
  279.         self.default_opts = {
  280.             "category": "*",
  281.             "fullRegex": 0,
  282.             "earlyOut": 0,
  283.             "nameOnly": 0
  284.             }
  285.  
  286.     def parseArgs(self, args):
  287.  
  288.         query = []
  289.         need_help = 0
  290.         opts = self.default_opts
  291.         skip = 0
  292.         
  293.         for i in xrange(len(args)):
  294.  
  295.             if skip:
  296.                 skip -= 1
  297.                 continue
  298.             x = args[i]
  299.             
  300.             if x in ["-h","--help"]:
  301.                 need_help = 1
  302.                 break
  303.             elif x in ["-c", "--category"]:
  304.                 opts["category"] = args[i+1]
  305.                 skip = 1
  306.             elif x in ["-e", "--earlyout"]:
  307.                 opts["earlyOut"] = 1
  308.             elif x in ["-f", "--full-regex"]:
  309.                 opts["fullRegex"] = 1
  310.             elif x in ["-n", "--name-only"]:
  311.                 opts["nameOnly"] = 1
  312.             else:
  313.                 query.append(x)
  314.  
  315.         if need_help or query == []:
  316.             print_info(0, self.longHelp())
  317.             sys.exit(-1)
  318.             
  319.         return (query, opts)
  320.                 
  321.     def perform(self, args):
  322.         (query, opts) = self.parseArgs(args)
  323.  
  324.         if opts["fullRegex"]:
  325.             q = query
  326.         else:
  327.             q = map(lambda x: ((len(x) and x[0] == "/") and "^" or "/")
  328.                                + re.escape(x) + "$", query)
  329.         try:
  330.             q = string.join(q, "|")
  331.             rx = re.compile(q)
  332.         except:
  333.             die(2, "The query '" + pp.regexpquery(q) + "' does not appear to be a valid regular expression")
  334.  
  335.         # Pick out only selected categories
  336.         cat = opts["category"]
  337.         filter_fn = None
  338.         if cat != "*":
  339.             filter_fn = lambda x: x.find(cat+"/")==0
  340.  
  341.         if not Config["piping"] and Config["verbosityLevel"] >= 3:
  342.             print_info(3, "[ Searching for file(s) " + pp.regexpquery(string.join(query,",")) + " in " + pp.cpv(cat) + "... ]")
  343.         
  344.         matches = portage.db["/"]["vartree"].dbapi.cpv_all()
  345.         #matches = gentoolkit.find_all_installed_packages(filter_fn)
  346.  
  347.         found = 0
  348.  
  349.         def dumpToPipe(pkg):
  350.             mysplit = pkg.split("/")
  351.             cnt = portage.dblink(mysplit[0], mysplit[1], "/", gentoolkit.settings).getcontents()
  352.             #cnt = pkg.get_contents()
  353.             if not cnt: return
  354.             for file in cnt.keys():
  355.                 if rx.search(file) and (opts["category"] == "*" or portage.catpkgsplit(pkg)[0] == opts["category"]):
  356.                     if opts["nameOnly"]:
  357.                         x = portage.catpkgsplit(pkg)
  358.                         print x[0]+"/"+x[1]
  359.                     else:
  360.                         print pkg
  361.                     return
  362.  
  363.         class DummyExp:
  364.             pass
  365.             
  366.         def dumpToScreen(pkg):
  367.             mysplit = pkg.split("/")
  368.             cnt = portage.dblink(mysplit[0], mysplit[1], "/", gentoolkit.settings).getcontents()
  369.             #cnt = pkg.get_contents()
  370.             if not cnt: return
  371.             for file in cnt.keys():
  372.                 if rx.search(file) and (opts["category"] == "*" or portage.catpkgsplit(pkg)[0] == opts["category"]):
  373.                     if opts["nameOnly"]:
  374.                         x = portage.catpkgsplit(pkg)
  375.                         s = x[0]+"/"+x[1]
  376.                     else:
  377.                         s = pkg
  378.                     s += " (" + pp.path(fileAsStr(file, cnt[file])) + ")"
  379.                     print_info(0, s)
  380.                     if opts["earlyOut"]:
  381.                         raise DummyExp
  382.  
  383.         try: 
  384.             if Config["piping"]:
  385.                 map(dumpToPipe, matches)
  386.             else:
  387.                 map(dumpToScreen, matches)
  388.         except DummyExp:
  389.             pass
  390.             
  391.     def shortHelp(self):
  392.         return pp.localoption("<local-opts> ") + pp.path("files...") + " - list all packages owning " + pp.path("files...")
  393.     def longHelp(self):
  394.         return "List all packages owning a particular set of files" + \
  395.                "\n" + \
  396.                "\n" + \
  397.                pp.emph("Note: ") + "Normally, only one package will own a file. If multiple packages own the same file, it usually consitutes a problem, and should be reported.\n" + \
  398.                "\n" + \
  399.                "Syntax:\n" + \
  400.                "  " + pp.command("belongs") + pp.localoption(" <local-opts> ") + pp.path("filename") + \
  401.                "\n" + \
  402.                pp.localoption("<local-opts>") + " is either of: \n" + \
  403.                     "  " + pp.localoption("-c, --category cat") + " - only search in category " + \
  404.                         pp.pkgquery("cat") + "\n" + \
  405.                     "  " + pp.localoption("-f, --full-regex") + "   - supplied query is a regex\n" + \
  406.                     "  " + pp.localoption("-e, --earlyout") + "     - stop when first match is found\n" + \
  407.                     "  " + pp.localoption("-n, --name-only") + "    - don't print the version."
  408.  
  409. class CmdDisplayUSEs(Command):
  410.     """Advanced report of a package's USE flags"""
  411.     def __init__(self):
  412.         self.default_opts = {
  413.             "installedOnly" : True
  414.             }
  415.     def parseArgs(self, args):
  416.  
  417.         query = ""
  418.         need_help = 0
  419.         opts = self.default_opts
  420.         skip = 0
  421.         
  422.         for i in xrange(len(args)):
  423.  
  424.             if skip:
  425.                 skip -= 1
  426.                 continue
  427.             x = args[i]
  428.             
  429.             if x in ["-h","--help"]:
  430.                 need_help = 1
  431.                 break
  432.             elif x in ["-a", "--all"]:
  433.                 opts["installedOnly"] = False
  434.             else:
  435.                 query = x
  436.  
  437.         if need_help or query == "":
  438.             print_info(0, self.longHelp())
  439.             sys.exit(-1)
  440.             
  441.         return (query, opts)
  442.  
  443.     def perform(self, args):
  444.  
  445.         (query, opts) = self.parseArgs(args)
  446.  
  447.         if not Config["piping"] and Config["verbosityLevel"] >= 3:
  448.             print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]")
  449.         
  450.         if opts["installedOnly"]:
  451.             matches = gentoolkit.find_installed_packages(query, True)
  452.         else:
  453.             matches = gentoolkit.find_packages(query, True)
  454.  
  455.         if not matches:
  456.             die(3, "No matching packages found for \"" + pp.pkgquery(query) + "\"")
  457.  
  458.  
  459.         useflags = gentoolkit.settings["USE"].split()    
  460.         usedesc = {}
  461.         uselocaldesc = {}
  462.  
  463.         # Load global USE flag descriptions
  464.         try:
  465.             fd = open(gentoolkit.settings["PORTDIR"]+"/profiles/use.desc")
  466.             usedesc = {}
  467.             for line in fd.readlines():
  468.                 if line[0] == "#":
  469.                     continue
  470.                 fields = line.split(" - ", 1)
  471.                 if len(fields) == 2:
  472.                     usedesc[fields[0].strip()] = fields[1].strip()
  473.         except IOError:
  474.             print_warn(5, "Could not load USE flag descriptions from " + ppath(gentoolkit.settings["PORTDIR"] + "/profiles/use.desc"))
  475.  
  476.         # Load local USE flag descriptions
  477.         try:
  478.             fd = open(gentoolkit.settings["PORTDIR"]+"/profiles/use.local.desc")
  479.             for line in fd.readlines():
  480.                 if line[0] == "#":
  481.                     continue
  482.                 fields = line.split(" - ", 1)
  483.                 if len(fields) == 2:
  484.                     catpkguse = re.search("(.*):(.*)", fields[0])
  485.                     if catpkguse:
  486.                         if not uselocaldesc.has_key(catpkguse.group(1).strip()):
  487.                             uselocaldesc[catpkguse.group(1).strip()] = {catpkguse.group(2).strip() : fields[1].strip()}
  488.                         else:
  489.                             uselocaldesc[catpkguse.group(1).strip()][catpkguse.group(2).strip()] = fields[1].strip()
  490.         except IOError:
  491.                 print_warn(5, "Could not load USE flag descriptions from " + path(gentoolkit.settings["PORTDIR"] + "/profiles/use.local.desc"))
  492.  
  493.         if not Config["piping"] and Config["verbosityLevel"] >= 3: 
  494.             print_info(3, "[ Colour Code : " + pp.useflagon("set") + " " + pp.useflagoff("unset") + " ]")
  495.             print_info(3, "[ Legend    : Left column  (U) - USE flags from make.conf              ]")
  496.             print_info(3, "[           : Right column (I) - USE flags packages was installed with ]")
  497.  
  498.         # Iterate through matches, printing a report for each package
  499.         matches_found = 0
  500.         for p in matches:
  501.  
  502.             if not p.is_installed() and opts["installedOnly"]:
  503.                 continue
  504.     
  505.             matches_found += 1
  506.             
  507.             bestver = p.get_cpv()
  508.             iuse = p.get_env_var("IUSE")
  509.           
  510.             if iuse:
  511.                 # Fix Bug #91623 by making sure the list of USE flags is unique
  512.                 # Added sort to make output prettier
  513.                 usevar = unique_array(iuse.split())
  514.                 usevar.sort()
  515.             else:
  516.                 usevar = []
  517.  
  518.             inuse = []
  519.             if p.is_installed():
  520.                 used = p.get_use_flags().split()
  521.             else:
  522.                 # cosmetic issue here as noninstalled packages don't have "used" flags
  523.                 used = useflags
  524.  
  525.             # store (inuse, inused, flag, desc)
  526.             output = []
  527.  
  528.             for u in usevar:
  529.                 inuse = 0
  530.                 inused = 0
  531.                 try:
  532.                     desc = usedesc[u]
  533.                 except KeyError:
  534.                     try:
  535.                         desc = uselocaldesc[p.get_category() + "/" + p.get_name()][u]
  536.                     except KeyError:
  537.                         desc = ""
  538.  
  539.                 if u in p.get_settings("USE").split():
  540.                     inuse = 1
  541.                 if u in used:
  542.                     inused = 1
  543.  
  544.                 output.append((inuse, inused, u, desc))
  545.  
  546.             # pretty print
  547.             if output:
  548.                 if not Config["piping"] and Config["verbosityLevel"] >= 3:
  549.                     print_info(0, "[ Found these USE variables for " + pp.cpv(bestver) + " ]")
  550.                     print_info(3, pp.emph(" U I"))
  551.                 maxflag_len = 0
  552.                 for inuse, inused, u, desc in output:
  553.                     if len(u) > maxflag_len:
  554.                         maxflag_len = len(u)
  555.  
  556.                 for in_makeconf, in_installed, flag, desc in output:
  557.                     markers = ["-","+"]
  558.                     colour = [pp.useflagoff, pp.useflagon]
  559.                     if Config["piping"]:
  560.                         print_info(0, markers[in_makeconf] + flag)
  561.                     else:
  562.                         if in_makeconf != in_installed:
  563.                             print_info(0, pp.emph(" %s %s" % (markers[in_makeconf], markers[in_installed])), False)
  564.                         else:
  565.                             print_info(0, " %s %s" % (markers[in_makeconf], markers[in_installed]), False)
  566.     
  567.                         print_info(0, " " + colour[in_makeconf](flag.ljust(maxflag_len)), False)
  568.  
  569.                         # print description
  570.                         if desc:
  571.                             print_info(0, " : " + desc)
  572.                         else:
  573.                             print_info(0, " : <unknown>")
  574.             else:
  575.                 if not Config["piping"] and Config["verbosityLevel"] >= 3:
  576.                     print_info(1, "[ No USE flags found for " + pp.cpv(p.get_cpv()) + "]")
  577.  
  578.         if Config["verbosityLevel"] >= 2:
  579.             if matches_found == 0:
  580.                 s = ""
  581.                 if opts["installedOnly"]:
  582.                     s = "installed "
  583.                 die(3, "No " + s + "packages found for " + pp.pkgquery(query))
  584.                 
  585.                     
  586.     def shortHelp(self):
  587.         return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - display USE flags for " + pp.pkgquery("pkgspec")
  588.     def longHelp(self):
  589.         return "Display USE flags for a given package\n" + \
  590.                "\n" + \
  591.                "Syntax:\n" + \
  592.                "  " + pp.command("uses") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
  593.                "\n" + \
  594.                pp.localoption("<local-opts>") + " is: \n" + \
  595.                "  " + pp.localoption("-a, --all") + " - include non-installed packages\n"
  596.  
  597.  
  598. class CmdDisplayDepGraph(Command):
  599.     """Display tree graph of dependencies for a query"""
  600.  
  601.     def __init__(self):
  602.         self.default_opts = {
  603.             "displayUSEFlags": 1,
  604.             "fancyFormatting": 1
  605.             }
  606.  
  607.     def parseArgs(self, args):
  608.  
  609.         query = ""
  610.         need_help = 0
  611.         opts = self.default_opts
  612.         skip = 0
  613.         
  614.         for i in xrange(len(args)):
  615.  
  616.             if skip:
  617.                 skip -= 1
  618.                 continue
  619.             x = args[i]
  620.             
  621.             if x in ["-h","--help"]:
  622.                 need_help = 1
  623.                 break
  624.             elif x in ["-U","--no-useflags"]:
  625.                 opts["displayUSEFlags"] = 0
  626.             elif x in ["-l","--linear"]:
  627.                 opts["fancyFormatting"] = 0
  628.             else:
  629.                 query = x
  630.  
  631.         if need_help or query == "":
  632.             print_info(0, self.longHelp())
  633.             sys.exit(-1)
  634.             
  635.         return (query, opts)
  636.  
  637.     def perform(self, args):
  638.         (query, opts) = self.parseArgs(args)
  639.  
  640.         if not Config["piping"] and Config["verbosityLevel"] >= 3:
  641.             print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]")
  642.  
  643.         matches = gentoolkit.find_packages(query, True)
  644.  
  645.         for pkg in matches:
  646.             if not pkg.is_installed():
  647.                 continue
  648.             if not Config["piping"] and Config["verbosityLevel"] >= 3:
  649.                 print_info(3, pp.section("* ") + "dependency graph for " + pp.cpv(pkg.get_cpv()))
  650.             else:
  651.                 print_info(0, pkg.get_cpv() + ":")
  652.  
  653.             stats = { "maxdepth": 0, "packages": 0 }
  654.             self._graph(pkg, opts, stats, 0, [], "")
  655.             if not Config["piping"] and Config["verbosityLevel"] >= 3:
  656.                 print_info(0, "[ " + pp.cpv(pkg.get_cpv()) + " stats: packages (" + pp.number(str(stats["packages"])) + \
  657.                 "), max depth (" + pp.number(str(stats["maxdepth"])) + ") ]")
  658.         
  659.     def _graph(self, pkg, opts, stats, level=0, pkgtbl=[], suffix=""):
  660.     
  661.         stats["packages"] += 1
  662.         stats["maxdepth"] = max(stats["maxdepth"], level)
  663.         
  664.         cpv = pkg.get_cpv()
  665.  
  666.         pfx = ""
  667.         if opts["fancyFormatting"]:
  668.             pfx = level * " " + "`-- " 
  669.         print_info(0, pfx + cpv + suffix)
  670.         
  671.         pkgtbl.append(cpv)
  672.         
  673.         pkgdeps = pkg.get_runtime_deps() + pkg.get_compiletime_deps()
  674.         for x in pkgdeps:
  675.             suffix = ""
  676.             cpv = x[2]
  677.             pkg = gentoolkit.find_best_match(x[0] + cpv)
  678.             if not pkg:
  679.                 continue
  680.             if pkg.get_cpv() in pkgtbl:
  681.                 continue
  682.             if cpv.find("virtual") == 0:
  683.                 suffix += " (" + pp.cpv(cpv) + ")"
  684.             if len(x[1]) and opts["displayUSEFlags"]:
  685.                 suffix += " [ " + pp.useflagon(string.join(x[1])) + " ]"
  686.             pkgtbl = self._graph(pkg, opts, stats, level+1, pkgtbl, suffix)
  687.         return pkgtbl
  688.  
  689.     def shortHelp(self):
  690.         return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - display a dependency tree for " + pp.pkgquery("pkgspec")
  691.     def longHelp(self):
  692.         return "Display a dependency tree for a given package\n" + \
  693.                "\n" + \
  694.                "Syntax:\n" + \
  695.                "  " + pp.command("depgraph") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
  696.                "\n" + \
  697.                pp.localoption("<local-opts>") + " is either of: \n" + \
  698.                "  " + pp.localoption("-U, --no-useflags") + " - do not show USE flags\n" + \
  699.                "  " + pp.localoption("-l, --linear") + "      - do not use fancy formatting"
  700.  
  701. class CmdDisplaySize(Command):
  702.     """Display disk size consumed by a package"""
  703.     def __init__(self):
  704.         self.default_opts = {
  705.             "reportSizeInBytes": 0
  706.             }
  707.  
  708.     def parseArgs(self, args):
  709.  
  710.         query = ""
  711.         need_help = 0
  712.         opts = self.default_opts
  713.         skip = 0
  714.         
  715.         for i in xrange(len(args)):
  716.  
  717.             if skip:
  718.                 skip -= 1
  719.                 continue
  720.             x = args[i]
  721.             
  722.             if x in ["-h","--help"]:
  723.                 need_help = 1
  724.                 break
  725.             elif x in ["-b","--bytes"]:
  726.                 opts["reportSizeInBytes"] = 1
  727.             else:
  728.                 query = x
  729.  
  730.         if need_help or query == "":
  731.             print_info(0, self.longHelp())
  732.             sys.exit(-1)
  733.             
  734.         return (query, opts)
  735.                 
  736.     def perform(self, args):
  737.         (query, opts) = self.parseArgs(args)
  738.  
  739.         if not Config["piping"] and Config["verbosityLevel"] >= 3:
  740.             print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]")
  741.  
  742.         matches = gentoolkit.find_packages(query, True)
  743.  
  744.         for pkg in matches:
  745.             if not pkg.is_installed():
  746.                 continue
  747.  
  748.             (size, files, uncounted) = pkg.size()
  749.  
  750.             if Config["piping"]:
  751.                 print_info(0, pkg.get_cpv() + ": total(" + str(files) + "), inaccessible(" + str(uncounted) + \
  752.                     "), size(" + str(size) + ")")
  753.             else:
  754.                 print_info(0, pp.section("* ") + "size of " + pp.cpv(pkg.get_cpv()))
  755.                 print_info(0, string.rjust(" Total files : ",25) + pp.number(str(files)))
  756.     
  757.                 if uncounted:
  758.                     print_info(0, string.rjust(" Inaccessible files : ",25) + pp.number(str(uncounted)))
  759.     
  760.                 sz = "%.2f KiB" % (size/1024.0)                
  761.                 if opts["reportSizeInBytes"]:
  762.                     sz = pp.number(str(size)) + " bytes"
  763.                 
  764.                 print_info(0, string.rjust("Total size  : ",25) + pp.number(sz))
  765.  
  766.                     
  767.     def shortHelp(self):
  768.         return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - print size of files contained in package " + pp.pkgquery("pkgspec")
  769.     def longHelp(self):
  770.         return "Print size total size of files contained in a given package" + \
  771.                "\n" + \
  772.                "Syntax:\n" + \
  773.                "  " + pp.command("size") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
  774.                "\n" + \
  775.                pp.localoption("<local-opts>") + " is: \n" + \
  776.                "  " + pp.localoption("-b, --bytes") + " - report size in bytes\n"
  777.  
  778. class CmdDisplayChanges(Command):
  779.     """Display changes for pkgQuery"""
  780.     pass
  781.  
  782. class CheckException:
  783.     def __init__(self, s):
  784.         self.s = s
  785.     
  786. class CmdCheckIntegrity(Command):
  787.     """Check timestamps and md5sums for files owned by pkgspec"""
  788.     def __init__(self):
  789.         self.default_opts = {
  790.             "showSummary" : 1,
  791.             "showGoodFiles" : 0,
  792.             "showBadFiles" : 1,
  793.             "checkTimestamp" : 1,
  794.             "checkMD5sum": 1
  795.             }
  796.  
  797.     def parseArgs(self, args):
  798.  
  799.         query = ""
  800.         need_help = 0
  801.         opts = self.default_opts
  802.         skip = 0
  803.         
  804.         for i in xrange(len(args)):
  805.             if skip:
  806.                 skip -= 1
  807.                 continue
  808.             x = args[i]
  809.             
  810.             if x in ["-h","--help"]:
  811.                 need_help = 1
  812.                 break
  813.             else:
  814.                 query = x
  815.  
  816.         if need_help or query == "":
  817.             print_info(0, self.longHelp())
  818.             sys.exit(-1)
  819.             
  820.         return (query, opts)
  821.  
  822.     def getMD5sum(self, file):
  823.         return portage_checksum.perform_md5(file, calc_prelink=1)
  824.     
  825.     def perform(self, args):
  826.         (query, opts) = self.parseArgs(args)
  827.  
  828.         matches = gentoolkit.find_packages(query, True)
  829.  
  830.         for pkg in matches:
  831.             if not pkg.is_installed():
  832.                 continue
  833.             if not Config["piping"] and Config["verbosityLevel"] >= 3:
  834.                 print_info(1, "[ Checking " + pp.cpv(pkg.get_cpv()) + " ]")
  835.             else:
  836.                 print_info(0, pkg.get_cpv() + ":")
  837.                 
  838.             files = pkg.get_contents()
  839.             checked_files = 0
  840.             good_files = 0
  841.             for file in files.keys():
  842.                 type = files[file][0]
  843.                 try:
  844.                     st = os.lstat(file)
  845.                     if type == "dir":
  846.                         if not os.path.isdir(file):
  847.                             raise CheckException(file + " exists, but is not a directory")
  848.                     elif type == "obj":
  849.                         mtime = files[file][1]
  850.                         md5sum = files[file][2]
  851.                         if opts["checkMD5sum"]:
  852.                             try: 
  853.                                 actual_checksum = self.getMD5sum(file)
  854.                             except:
  855.                                 raise CheckException("Failed to calculate MD5 sum for " + file)
  856.                                 
  857.                             if self.getMD5sum(file) != md5sum:
  858.                                 raise CheckException(file + " has incorrect md5sum")
  859.                         if opts["checkTimestamp"]:
  860.                             if st.st_mtime != int(mtime):
  861.                                 raise CheckException(file + (" has wrong mtime (is %d, should be %s)" % (st.st_mtime, mtime)))
  862.                     elif type == "sym":
  863.                         # FIXME: nastry strippery; portage should have this fixed!
  864.                         t = files[file][2]
  865.                         target = os.path.normpath(t.strip())
  866.                         if not os.path.islink(file):
  867.                             raise CheckException(file + " exists, but is not a symlink")
  868.                         tgt = os.readlink(file)
  869.                         if tgt != target:
  870.                             raise CheckException(file + " does not point to " + target)
  871.                     elif type == "fif":
  872.                         pass
  873.                     else:
  874.                         pp.print_error(file)
  875.                         pp.print_error(files[file])
  876.                         pp.print_error(type)
  877.                         raise CheckException(file + " has unknown type " + type)
  878.                     good_files += 1
  879.                 except CheckException, (e):
  880.                     print_error(e.s)
  881.                 except OSError:
  882.                     print_error(file + " does not exist")
  883.                 checked_files += 1
  884.             print_info(0, pp.section(" * ") + pp.number(str(good_files)) + " out of " +  pp.number(str(checked_files)) + " files good")
  885.                     
  886.     def shortHelp(self):
  887.         return pp.pkgquery("pkgspec") + " - check MD5sums and timestamps of " + pp.pkgquery("pkgspec") + "'s files"
  888.     def longHelp(self):
  889.         return "Check package's files against recorded MD5 sums and timestamps" + \
  890.                 "\n" + \
  891.                 "Syntax:\n" + \
  892.                 "  " + pp.command("check") + pp.pkgquery(" pkgspec")
  893.  
  894. class CmdDisplayStatistics(Command):
  895.     """Display statistics about installed and uninstalled packages"""
  896.     pass
  897.  
  898. class CmdWhich(Command):
  899.     """Display the filename of the ebuild for a given package
  900.        that would be used by Portage with the current configuration."""
  901.     def __init__(self):
  902.         self.default_opts = {
  903.             "includeMasked": False
  904.             }
  905.  
  906.     def parseArgs(self, args):
  907.  
  908.         query = ""
  909.         need_help = 0
  910.         opts = self.default_opts
  911.         skip = 0
  912.         
  913.         for i in xrange(len(args)):
  914.  
  915.             if skip:
  916.                 skip -= 1
  917.                 continue
  918.             x = args[i]
  919.             
  920.             if x in ["-h","--help"]:
  921.                 need_help = 1
  922.                 break
  923.             elif x in ["-m", "--include-masked"]:
  924.                 opts["includeMasked"] = True
  925.             else:
  926.                 query = x
  927.  
  928.         if need_help or query == "":
  929.             print_info(0, self.longHelp())
  930.             sys.exit(-1)
  931.             
  932.         return (query, opts)
  933.                 
  934.     def perform(self, args):
  935.         (query, opts) = self.parseArgs(args)
  936.  
  937.         matches = gentoolkit.find_packages(query, opts["includeMasked"])
  938.         matches = gentoolkit.sort_package_list(matches)
  939.  
  940.         if matches:
  941.             print_info(0, os.path.normpath(matches[-1].get_ebuild_path()))
  942.         else:
  943.             print_error("No masked or unmasked packages found for " + pp.pkgquery(query))
  944.                     
  945.     def shortHelp(self):
  946.         return pp.pkgquery("pkgspec") + " - print full path to ebuild for package " + pp.pkgquery("pkgspec")
  947.     def longHelp(self):
  948.         # Not documenting --include-masked at this time, since I'm not sure that it is needed. - FuzzyRay
  949.         return "Print full path to ebuild for a given package" + \
  950.             "\n" + \
  951.             "Syntax:\n" + \
  952.             "  " + pp.command("which ") + pp.pkgquery("pkgspec")
  953.  
  954. class CmdListGLSAs(Command):
  955.     """List outstanding GLSAs."""
  956.     pass
  957.  
  958. class CmdListDepends(Command):
  959.     """List all packages directly or indirectly depending on pkgQuery"""
  960.     def __init__(self):
  961.         self.default_opts = {
  962.             "onlyDirect": 1,
  963.             "onlyInstalled": 1
  964.             }
  965.  
  966.     def parseArgs(self, args):
  967.  
  968.         query = ""
  969.         need_help = 0
  970.         opts = self.default_opts
  971.         skip = 0
  972.          
  973.         for i in xrange(len(args)):
  974.             if skip:
  975.                 skip -= 1
  976.                 continue
  977.             x = args[i]
  978.              
  979.             if x in ["-h","--help"]:
  980.                 need_help = 1
  981.                 break
  982.             elif x in ["-d", "--direct"]:
  983.                 opts["onlyDirect"] = 1
  984.             elif x in ["-D", "--indirect"]:
  985.                 opts["onlyDirect"] = 0
  986.             elif x in ["-a", "--all-packages"]:
  987.                 opts["onlyInstalled"] = 0
  988.             else:
  989.                 query = x
  990.      
  991.         if need_help or query == "":
  992.             print self.longHelp()
  993.             sys.exit(-1)
  994.         return (query, opts)
  995.  
  996.     def perform(self, args):
  997.         def subdeps(cpv, spacing):
  998.             "Find subdeps of a package"
  999.             cpvs=gentoolkit.split_package_name(cpv) 
  1000.  
  1001.             catname = cpvs[0]+"/"+cpvs[1]
  1002.             if depscache.has_key(catname):
  1003.                 isdep = 0
  1004.                 for dep in depscache[catname]:
  1005.                     pkg = dep[0]
  1006.                     x = dep[1]
  1007.                     if string.find(x[2],"/") != -1 and \
  1008.                            portage.match_from_list(x[0]+x[2], [cpv]):
  1009.                         if x[1]:
  1010.                             print spacing+pkg.get_cpv(),
  1011.                             if Config["verbosityLevel"] >= 4:
  1012.                                 print " (" +string.join(x[1],"&")+ " ? " + x[0]+x[2] + ")"
  1013.                             else:
  1014.                                 print
  1015.                         else:
  1016.                             print spacing+pkg.get_cpv(),
  1017.                             if Config["verbosityLevel"] >= 4:
  1018.                                 print " (" + x[0]+x[2] + ")"
  1019.                             else:
  1020.                                 print
  1021.                         isdep = 1
  1022.                     if isdep:
  1023.                         subdeps(pkg.get_cpv(), spacing+" ")
  1024.   
  1025.         (query, opts) = self.parseArgs(args)
  1026.  
  1027.         if not Config["piping"] and Config["verbosityLevel"] >= 3:
  1028.             print_info(3, "[ Searching for packages depending on " + pp.pkgquery(query) + "... ]")
  1029.  
  1030.         isdepend = gentoolkit.split_package_name(query)
  1031.         
  1032.         if opts["onlyInstalled"]:
  1033.             packages = gentoolkit.find_all_installed_packages()
  1034.         else:
  1035.             packages = gentoolkit.find_all_packages()
  1036.  
  1037.         if not opts["onlyDirect"]:
  1038.             print_info(4, "Caching indirect dependencies...")
  1039.             depscache = {"":[]}
  1040.             for pkg in packages:
  1041.                 print_info(1, pp.section("* ") + "Dependencies for " + pp.cpv(pkg.get_cpv()) + ":")
  1042.  
  1043.                 try:
  1044.                     deps = pkg.get_runtime_deps() + pkg.get_compiletime_deps()
  1045.                 except KeyError, e:
  1046.                     # If the ebuild is not found... 
  1047.                     continue
  1048.                 for x in deps:
  1049.                     cpvs=gentoolkit.split_package_name(x[2]) 
  1050.                     #print cpvs
  1051.                     catname = cpvs[0]+"/"+cpvs[1]
  1052.                     if depscache.has_key(catname):
  1053.                         depscache[catname].append((pkg,x))
  1054.                     else:
  1055.                         depscache[catname] = [(pkg,x)]
  1056.             print "done"
  1057.  
  1058.         for pkg in packages:
  1059.             try:
  1060.                 deps = pkg.get_runtime_deps() + pkg.get_compiletime_deps()
  1061.             except KeyError, e:
  1062.                 # If the ebuild is not found... 
  1063.                 continue
  1064.             isdep = 0
  1065.             for x in deps:
  1066.                 cpvs=gentoolkit.split_package_name(x[2])
  1067.                 cat_match=0
  1068.                 ver_match=0
  1069.                 name_match=0
  1070.                 if not isdepend[0] or \
  1071.                     cpvs[0] == isdepend[0]:
  1072.                     cat_match=1
  1073.                 if not isdepend[2] or \
  1074.                     ( cpvs[2] == isdepend[2] and \
  1075.                       (isdepend[3] or \
  1076.                        isdepend[3] == "r0" or \
  1077.                        cpvs[3] == isdepend[3])):
  1078.                       ver_match=1
  1079.                 if cpvs[1] == isdepend[1]:
  1080.                     name_match=1
  1081.                 if cat_match and ver_match and name_match:
  1082.                     if not isdep:
  1083.                         if x[1]:
  1084.                             print pkg.get_cpv(),
  1085.                             if Config["verbosityLevel"] >= 4:
  1086.                                    print " (" +string.join(x[1],"&")+ " ? " + x[0]+x[2] + ")"
  1087.                             else:
  1088.                                 print
  1089.                         else:
  1090.                             print pkg.get_cpv(),
  1091.                             if Config["verbosityLevel"] >= 4:
  1092.                                 print " (" + x[0]+x[2] + ")"
  1093.                             else:
  1094.                                 print
  1095.                         isdep = 1
  1096.                     elif Config["verbosityLevel"] >= 4:
  1097.                         if x[1]:
  1098.                             print " "*len(pkg.get_cpv()) + " (" +string.join(x[1],"&")+ " ? " + x[0]+x[2] + ")"
  1099.                         else:
  1100.                             print " "*len(pkg.get_cpv()) + " (" + x[0]+x[2] + ")"    
  1101.             if isdep and not opts["onlyDirect"] :
  1102.                  subdeps(pkg.get_cpv(), " ")
  1103.  
  1104.  
  1105.     def shortHelp(self):
  1106.         return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - list all direct dependencies matching " + pp.pkgquery("pkgspec")
  1107.      
  1108.     def longHelp(self):
  1109.          return "List all direct dependencies matching a query pattern" + \
  1110.                 "\n" + \
  1111.                 "Syntax:\n" + \
  1112.                 "  " + pp.command("depends") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
  1113.                 "\n" + \
  1114.                 pp.localoption("<local-opts>") + " is either of: \n" + \
  1115.                 "  " + pp.localoption("-a, --all-packages") + " - search in all available packages (slow)\n" + \
  1116.                 "  " + pp.localoption("-d, --direct") + "       - search direct dependencies only (default)\n" + \
  1117.                 "  " + pp.localoption("-D, --indirect") + "     - search indirect dependencies (VERY slow)\n"
  1118.  
  1119.  
  1120. class CmdListPackages(Command):
  1121.     """List packages satisfying pkgQuery"""
  1122.     def __init__(self):
  1123.         self.default_opts = {
  1124.             "category": "*",
  1125.             "includeInstalled": 1,
  1126.             "includePortTree": 0,
  1127.             "includeOverlayTree": 0,
  1128.             "includeMasked": 1,
  1129.             "regex": 0,
  1130.             "exact": 0,
  1131.             "duplicates": 0
  1132.             }
  1133.  
  1134.     def parseArgs(self, args):
  1135.  
  1136.         query = ""
  1137.         need_help = 0
  1138.         opts = self.default_opts
  1139.         skip = 0
  1140.         
  1141.         for i in xrange(len(args)):
  1142.  
  1143.             if skip:
  1144.                 skip -= 1
  1145.                 continue
  1146.             x = args[i]
  1147.             
  1148.             if x in ["-h","--help"]:
  1149.                 need_help = 1
  1150.                 break
  1151.             elif x in ["-i", "--installed"]:
  1152.                 opts["includeInstalled"] = 1
  1153.             elif x in ["-I", "--exclude-installed"]:
  1154.                 opts["includeInstalled"] = 0
  1155.             elif x in ["-p", "--portage-tree"]:
  1156.                 opts["includePortTree"] = 1
  1157.             elif x in ["-o", "--overlay-tree"]:
  1158.                 opts["includeOverlayTree"] = 1
  1159.             elif x in ["-m", "--exclude-masked"]:
  1160.                 opts["includeMasked"] = 0
  1161.             elif x in ["-f", "--full-regex"]:
  1162.                 opts["regex"] = 1
  1163.             elif x in ["-e", "--exact-name"]:
  1164.                 opts["exact"] = 1
  1165.             elif x in ["-d", "--duplicates"]:
  1166.                 opts["duplicates"] = 1
  1167.             else:
  1168.                 query = x
  1169.  
  1170.         # Only search installed packages when listing duplicated packages
  1171.         if opts["duplicates"]:
  1172.             opts["includeInstalled"] = 1
  1173.             opts["includePortTree"] = 0
  1174.             opts["includeOverlayTree"] = 0
  1175.  
  1176.         if need_help:
  1177.             print_info(0, self.longHelp())
  1178.             sys.exit(-1)
  1179.             
  1180.         return (query, opts)
  1181.                 
  1182.     def perform(self, args):
  1183.         (query, opts) = self.parseArgs(args)
  1184.  
  1185.         rev = ""
  1186.         name = ""
  1187.         ver = ""
  1188.         cat = ""
  1189.  
  1190.         if query != "":
  1191.             (cat, name, ver, rev) = gentoolkit.split_package_name(query)
  1192.             if rev == "r0": rev = ""
  1193.  
  1194.         package_finder = None
  1195.  
  1196.         if opts["includeInstalled"] and (opts["includePortTree"] or opts["includeOverlayTree"]):
  1197.             package_finder = gentoolkit.find_all_packages
  1198.         elif opts["includeInstalled"]:
  1199.             package_finder = gentoolkit.find_all_installed_packages
  1200.         elif opts["includePortTree"] or opts["includeOverlayTree"]:
  1201.             package_finder = gentoolkit.find_all_uninstalled_packages
  1202.  
  1203.         if not package_finder:
  1204.             die(2, "You must specify one of -i, -p or -o")
  1205.  
  1206.         filter_fn = None
  1207.  
  1208.         if Config["verbosityLevel"] >= 3:
  1209.             scat = "'" + cat + "'"
  1210.             if not cat:
  1211.                 scat = "all categories"
  1212.             sname = "package '" + name + "'"
  1213.             if not name:
  1214.                 sname = "all packages"
  1215.             if not Config["piping"] and Config["verbosityLevel"] >= 3:
  1216.                 print_info(1, "[ Searching for " + pp.cpv(sname) + " in " + pp.cpv(scat) + " among: ]")
  1217.  
  1218.         # replace empty strings with .* and escape regular expression syntax
  1219.         if query != "":
  1220.             if not opts["regex"]:
  1221.                 cat, name, ver, rev = [re.sub('^$', ".*", re.escape(x)) for x in cat, name, ver, rev]
  1222.             else:
  1223.                 cat, name, ver, rev = [re.sub('^$', ".*", x) for x in cat, name, ver, rev]
  1224.  
  1225.             try:
  1226.                 if opts["exact"]:
  1227.                     filter_fn = lambda x: re.match(cat+"/"+name, x)
  1228.                 else:
  1229.                     filter_fn = lambda x: re.match(cat+"/.*"+name, x)
  1230.                 matches = package_finder(filter_fn)
  1231.             except:
  1232.                 die(2, "The query '" + pp.regexpquery(query) + "' does not appear to be a valid regular expression")
  1233.         else:
  1234.             cat, name, ver, rev = [re.sub('^$', ".*", x) for x in cat, name, ver, rev]
  1235.             filter_fn = lambda x: True
  1236.             matches = package_finder(filter_fn)
  1237.  
  1238.         # Find duplicate packages
  1239.         if opts["duplicates"]:
  1240.             dups = {}
  1241.             newmatches = []
  1242.             for pkg in matches:
  1243.                 mykey = pkg.get_category() + "/" + pkg.get_name() 
  1244.                 if dups.has_key(mykey):
  1245.                     dups[mykey].append(pkg)
  1246.                 else:
  1247.                     dups[mykey] = [pkg]
  1248.  
  1249.             for mykey in dups.keys():
  1250.                 if len(dups[mykey]) > 1:
  1251.                     newmatches += dups[mykey]
  1252.  
  1253.             matches = newmatches
  1254.  
  1255.         matches = gentoolkit.sort_package_list(matches)
  1256.  
  1257.         # If no version supplied, fix regular expression
  1258.         if ver == ".*": ver = "[0-9]+[^-]*"
  1259.  
  1260.         if rev != ".*":    # revision supplied
  1261.             ver = ver + "-" + rev
  1262.  
  1263.         if opts["exact"]:
  1264.             rx = re.compile(cat + "/" + name + "-" + ver)
  1265.         else:
  1266.             rx = re.compile(cat + "/.*" + name + ".*-" + ver)
  1267.  
  1268.         if opts["includeInstalled"]:
  1269.             self._print_installed(matches, rx)
  1270.         
  1271.         if opts["includePortTree"]:
  1272.             self._print_porttree(matches, rx)
  1273.         
  1274.         if opts["includeOverlayTree"]:
  1275.             self._print_overlay(matches, rx)
  1276.  
  1277.     def _get_mask_status(self, pkg):
  1278.         pkgmask = 0
  1279.         if pkg.is_masked():
  1280.             pkgmask = pkgmask + 3
  1281.         keywords = pkg.get_env_var("KEYWORDS").split()
  1282.         if "~" + gentoolkit.settings["ARCH"] in keywords:
  1283.             pkgmask = pkgmask + 1
  1284.         elif "-*" in keywords or "-" + gentoolkit.settings["ARCH"] in keywords:
  1285.             pkgmask = pkgmask + 2
  1286.         return pkgmask
  1287.  
  1288.     def _generic_print(self, header, exclude, matches, rx, status):
  1289.         if Config["verbosityLevel"] >= 3:
  1290.             print_info(1, header)
  1291.  
  1292.         pfxmodes = [ "---", "I--", "-P-", "--O" ]
  1293.         maskmodes = [ "  ", " ~", " -", "M ", "M~", "M-" ]
  1294.  
  1295.         for pkg in matches:
  1296.             if exclude(pkg):
  1297.                 continue
  1298.  
  1299.             pkgmask = self._get_mask_status(pkg)
  1300.             
  1301.             slot = pkg.get_env_var("SLOT")
  1302.  
  1303.             if rx.search(pkg.get_cpv()):
  1304.                 if Config["piping"]:
  1305.                     print_info(0, pkg.get_cpv())
  1306.                 else:
  1307.                     print_info(0, "[" + pp.installedflag(pfxmodes[status]) + "] [" + pp.maskflag(maskmodes[pkgmask]) + "] " + pp.cpv(pkg.get_cpv()) + " (" + pp.slot(slot) + ")")
  1308.  
  1309.     def _print_overlay(self, matches, rx):
  1310.         self._generic_print(
  1311.             pp.section(" *") + " overlay tree (" + pp.path(gentoolkit.settings["PORTDIR_OVERLAY"]) + ")",
  1312.             lambda x: not x.is_overlay(),
  1313.             matches, rx, 3)
  1314.             
  1315.     def _print_porttree(self, matches, rx):
  1316.         self._generic_print(
  1317.             pp.section(" *") + " Portage tree (" + pp.path(gentoolkit.settings["PORTDIR"]) + ")",
  1318.             lambda x: x.is_overlay() or x.is_installed(),
  1319.             matches, rx, 2)
  1320.                 
  1321.     def _print_installed(self, matches, rx):
  1322.         self._generic_print(
  1323.             pp.section(" *") + " installed packages",
  1324.             lambda x: not x.is_installed(),
  1325.             matches, rx, 1)
  1326.  
  1327.     def shortHelp(self):
  1328.         return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - list all packages matching " + pp.pkgquery("pkgspec")
  1329.     def longHelp(self):
  1330.         return "List all packages matching a query pattern" + \
  1331.                "\n" + \
  1332.                "Syntax:\n" + \
  1333.                "  " + pp.command("list") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
  1334.                "\n" + \
  1335.                pp.localoption("<local-opts>") + " is either of: \n" + \
  1336.                "  " + pp.localoption("-i, --installed") + "         - search installed packages (default)\n" + \
  1337.                "  " + pp.localoption("-I, --exclude-installed") + " - do not search installed packages\n" + \
  1338.                "  " + pp.localoption("-p, --portage-tree") + "      - also search in portage tree (" + gentoolkit.settings["PORTDIR"] + ")\n" + \
  1339.                "  " + pp.localoption("-o, --overlay-tree") + "      - also search in overlay tree (" + gentoolkit.settings["PORTDIR_OVERLAY"] + ")\n" + \
  1340.                "  " + pp.localoption("-f, --full-regex") + "        - query is a regular expression\n" + \
  1341.                "  " + pp.localoption("-e, --exact-name") + "        - list only those packages that exactly match\n" + \
  1342.                "  " + pp.localoption("-d, --duplicates") + "        - list only installed duplicate packages\n"
  1343.  
  1344. class CmdFindUSEs(Command):
  1345.     """Find all packages with a particular USE flag."""
  1346.     def __init__(self):
  1347.         self.default_opts = {
  1348.             "category": "*",
  1349.             "includeInstalled": 1,
  1350.             "includePortTree": 0,
  1351.             "includeOverlayTree": 0,
  1352.             "includeMasked": 1,
  1353.             "regex": 0
  1354.             }
  1355.  
  1356.     def parseArgs(self, args):
  1357.  
  1358.         query = ""
  1359.         need_help = 0
  1360.         opts = self.default_opts
  1361.         skip = 0
  1362.         
  1363.         for i in xrange(len(args)):
  1364.  
  1365.             if skip:
  1366.                 skip -= 1
  1367.                 continue
  1368.             x = args[i]
  1369.             
  1370.             if x in ["-h","--help"]:
  1371.                 need_help = 1
  1372.                 break
  1373.             elif x in ["-i", "--installed"]:
  1374.                 opts["includeInstalled"] = 1
  1375.             elif x in ["-I", "--exclude-installed"]:
  1376.                 opts["includeInstalled"] = 0
  1377.             elif x in ["-p", "--portage-tree"]:
  1378.                 opts["includePortTree"] = 1
  1379.             elif x in ["-o", "--overlay-tree"]:
  1380.                 opts["includeOverlayTree"] = 1
  1381.             elif x in ["-m", "--exclude-masked"]:
  1382.                 opts["includeMasked"] = 0
  1383.             else:
  1384.                 query = x
  1385.  
  1386.         if need_help:
  1387.             print_info(0, self.longHelp())
  1388.             sys.exit(-1)
  1389.             
  1390.         return (query, opts)
  1391.                 
  1392.     def perform(self, args):
  1393.         (query, opts) = self.parseArgs(args)
  1394.  
  1395.         rev = ".*"
  1396.         name = ".*"
  1397.         ver = ".*"
  1398.         cat = ".*"
  1399.         
  1400.         package_finder = None
  1401.  
  1402.         if opts["includeInstalled"] and (opts["includePortTree"] or opts["includeOverlayTree"]):
  1403.             package_finder = gentoolkit.find_all_packages
  1404.         elif opts["includeInstalled"]:
  1405.             package_finder = gentoolkit.find_all_installed_packages
  1406.         elif opts["includePortTree"] or opts["includeOverlayTree"]:
  1407.             package_finder = gentoolkit.find_all_uninstalled_packages
  1408.  
  1409.         if not package_finder:
  1410.             die(2,"You must specify one of -i, -p or -o")
  1411.  
  1412.         filter_fn = lambda x: True
  1413.             
  1414.         if Config["verbosityLevel"] >= 3:
  1415.             scat = "'" + cat + "'"
  1416.             if cat == ".*":
  1417.                 scat = "all categories"
  1418.             if not Config["piping"]:
  1419.                 print_info(2, "[ Searching for USE flag " + pp.useflag(query)  + " in " + pp.cpv(scat) + " among: ]")
  1420.                 if opts["includeInstalled"]:
  1421.                     print_info(1, pp.section(" *") + " installed packages")
  1422.                 if opts["includePortTree"]:
  1423.                     print_info(1, pp.section(" *") + " Portage tree (" + pp.path(gentoolkit.settings["PORTDIR"]) + ")")
  1424.                 if opts["includeOverlayTree"]:
  1425.                     print_info(1, pp.section(" *") + " overlay tree (" + pp.path(gentoolkit.settings["PORTDIR_OVERLAY"]) + ")")
  1426.         
  1427.         matches = package_finder(filter_fn)
  1428.  
  1429.         rx = re.compile(cat + "/" + name + "-" + ver + "(-" + rev + ")?")
  1430.         pfxmodes = [ "---", "I--", "-P-", "--O" ]
  1431.         maskmodes = [ "  ", " ~", " -", "M ", "M~", "M-" ]
  1432.         for pkg in matches:
  1433.             status = 0
  1434.             if pkg.is_installed():
  1435.                 status = 1
  1436.             elif pkg.is_overlay():
  1437.                 status = 3
  1438.             else:
  1439.                 status = 2
  1440.  
  1441.             useflags = pkg.get_env_var("IUSE").split()
  1442.  
  1443.             if query not in useflags:
  1444.                 continue 
  1445.                 
  1446.             # Determining mask status
  1447.             pkgmask = 0
  1448.             if pkg.is_masked():
  1449.                 pkgmask = pkgmask + 3
  1450.             keywords = pkg.get_env_var("KEYWORDS").split()
  1451.             if "~"+gentoolkit.settings["ARCH"] in keywords:
  1452.                 pkgmask = pkgmask + 1
  1453.             elif "-*" in keywords or "-"+gentoolkit.settings["ARCH"] in keywords:
  1454.                 pkgmask = pkgmask + 2
  1455.  
  1456.             # Determining SLOT value
  1457.             slot = pkg.get_env_var("SLOT")
  1458.  
  1459.             if (status == 1 and opts["includeInstalled"]) or \
  1460.                (status == 2 and opts["includePortTree"]) or \
  1461.                (status == 3 and opts["includeOverlayTree"]):
  1462.                 if Config["piping"]:
  1463.                     print_info(0, pkg.get_cpv())
  1464.                 else:
  1465.                     print_info(0, "[" + pp.installedflag(pfxmodes[status]) + "] [" + pp.maskflag(maskmodes[pkgmask]) + "] " + pp.cpv(pkg.get_cpv()) + " (" + pp.slot(slot) + ")")
  1466.                     
  1467.     def shortHelp(self):
  1468.         return pp.localoption("<local-opts> ") + pp.pkgquery("useflag") + " - list all packages with " + pp.pkgquery("useflag")
  1469.     def longHelp(self):
  1470.         return "List all packages with a particular USE flag" + \
  1471.                "\n" + \
  1472.                "Syntax:\n" + \
  1473.                "  " + pp.command("list") + pp.localoption(" <local-opts> ") + pp.pkgquery("useflag") + \
  1474.                "\n" + \
  1475.                pp.localoption("<local-opts>") + " is either of: \n" + \
  1476.                "  " + pp.localoption("-i, --installed") + "         - search installed packages (default)\n" + \
  1477.                "  " + pp.localoption("-I, --exclude-installed") + " - do not search installed packages\n" + \
  1478.                "  " + pp.localoption("-p, --portage-tree") + "      - also search in portage tree (" + gentoolkit.settings["PORTDIR"] + ")\n" + \
  1479.                "  " + pp.localoption("-o, --overlay-tree") + "      - also search in overlay tree (" + gentoolkit.settings["PORTDIR_OVERLAY"] + ")\n"
  1480.  
  1481. #
  1482. # Command line tokens to commands mapping
  1483. #
  1484.  
  1485. Known_commands = {
  1486.     "list" : CmdListPackages(),
  1487.     "files" : CmdListFiles(),
  1488.     "belongs" : CmdListBelongs(),
  1489.     "depends" : CmdListDepends(),
  1490.     "hasuse" : CmdFindUSEs(),
  1491.     "uses" : CmdDisplayUSEs(),
  1492.     "depgraph" : CmdDisplayDepGraph(),
  1493.     "changes" : CmdDisplayChanges(),
  1494.     "size" : CmdDisplaySize(),
  1495.     "check" : CmdCheckIntegrity(),
  1496.     "stats" : CmdDisplayStatistics(),
  1497.     "glsa" : CmdListGLSAs(),
  1498.     "which": CmdWhich()
  1499.     }
  1500.  
  1501. # Short command line tokens
  1502.  
  1503. Short_commands = {
  1504.     "a" : "glsa",
  1505.     "b" : "belongs",
  1506.     "c" : "changes",
  1507.     "d" : "depends",
  1508.     "f" : "files",
  1509.     "g" : "depgraph",
  1510.     "h" : "hasuse",
  1511.     "k" : "check",
  1512.     "l" : "list",
  1513.     "s" : "size",
  1514.     "t" : "stats",
  1515.     "u" : "uses",
  1516.     "w" : "which"
  1517. }
  1518.  
  1519. from gentoolkit import Config
  1520.  
  1521. Config = {
  1522.     # Query will include packages installed on the system
  1523.     "installedPackages":  1,
  1524.     # Query will include packages available for installation
  1525.     "uninstalledPackages": 0,
  1526.     # Query will include overlay packages (iff uninstalledPackages==1)
  1527.     "overlayPackages": 1,
  1528.     # Query will include masked packages (iff uninstalledPackages==1)
  1529.     "maskedPackages": 0,
  1530.     # Query will only consider packages in the following categories, empty means all.
  1531.     "categoryFilter": [],
  1532.     # Enable color output (-1 = use Portage setting, 0 = force off, 1 = force on)
  1533.     "color": -1,
  1534.     # Level of detail on the output
  1535.     "verbosityLevel": 3,
  1536.     # Query will display info for multiple SLOTed versions
  1537.     "considerDuplicates": 1,
  1538.     # Are we writing to a pipe?
  1539.     "piping": 0
  1540. }
  1541.     
  1542. def printVersion():
  1543.     """Print the version of this tool to the console."""
  1544.     print_info(0, __productname__ + "(" + __version__ + ") - " + \
  1545.           __description__)
  1546.     print_info(0, "Author(s): " + __author__)
  1547.  
  1548. def buildReverseMap(m):
  1549.     r = {}
  1550.     for x in m.keys():
  1551.         r[m[x]] = x
  1552.     return r
  1553.     
  1554. def printUsage():
  1555.     """Print full usage information for this tool to the console."""
  1556.     
  1557.     short_cmds = buildReverseMap(Short_commands);
  1558.     
  1559.     print_info(0, pp.emph("Usage: ") + pp.productname(__productname__) + pp.globaloption(" <global-opts> ") + pp.command("command") + pp.localoption(" <local-opts>"))
  1560.     print_info(0, "where " + pp.globaloption("<global-opts>") + " is one of")
  1561.     print_info(0, pp.globaloption(" -q, --quiet") + "   - minimal output")
  1562.     print_info(0, pp.globaloption(" -C, --nocolor") + " - turn off colours")
  1563.     print_info(0, pp.globaloption(" -h, --help") + "    - this help screen")
  1564.     print_info(0, pp.globaloption(" -V, --version") + " - display version info")
  1565.     print_info(0, pp.globaloption(" -N, --no-pipe") + " - turn off pipe detection")
  1566.     
  1567.     print_info(0, "where " + pp.command("command") + "(" + pp.command("short") + ") is one of")
  1568.     keys = Known_commands.keys()
  1569.     keys.sort()
  1570.     for x in keys:
  1571.         print_info(0, " " + pp.command(x) + "(" + pp.command(short_cmds[x]) + ") " + \
  1572.             Known_commands[x].shortHelp())
  1573.     print
  1574.     
  1575. def configure():
  1576.     """Set up default configuration.
  1577.     """
  1578.  
  1579.     # Guess colour output
  1580.     if (Config["color"] == -1 and \
  1581.         ((not sys.stdout.isatty()) or (gentoolkit.settings["NOCOLOR"] in ["yes","true"]))):
  1582.             pp.output.nocolor()
  1583.  
  1584.     # Guess piping output
  1585.     if not sys.stdout.isatty():
  1586.         Config["piping"] = True
  1587.     else:
  1588.         Config["piping"] = False
  1589.         
  1590.     
  1591. def parseArgs(args):
  1592.     """Parse tool-specific arguments. 
  1593.     
  1594.     Arguments are on the form equery <tool-specific> [command] <command-specific>
  1595.     
  1596.     This function will only parse the <tool-specific> bit.
  1597.     """
  1598.     command = None
  1599.     local_opts = []
  1600.     showhelp = 0
  1601.  
  1602.     def expand(x):
  1603.         if x in Short_commands.keys():
  1604.             return Short_commands[x]
  1605.         return x
  1606.         
  1607.     for i in xrange(len(args)):
  1608.         x = args[i]
  1609.         if 0:
  1610.             pass
  1611.         elif x in ["-h", "--help"]:
  1612.             showhelp = True
  1613.         elif x in ["-V", "--version"]:
  1614.             printVersion()
  1615.             sys.exit(0)
  1616.         elif x in ["-C", "--nocolor"]:
  1617.             Config["color"] = 0
  1618.             pp.output.nocolor()
  1619.         elif x in ["-N", "--no-pipe"]:
  1620.             Config["piping"] = False
  1621.         elif x in ["-q","--quiet"]:
  1622.             Config["verbosityLevel"] = 0
  1623.         elif expand(x) in Known_commands.keys():
  1624.             command = Known_commands[expand(x)]
  1625.             local_opts = args[i+1:]
  1626.             if showhelp:
  1627.                 local_opts.append("--help")
  1628.             break
  1629.         
  1630.     if not command and showhelp:
  1631.         printUsage()
  1632.         sys.exit(0)
  1633.  
  1634.     return (command, local_opts)
  1635.    
  1636. if __name__ == "__main__":
  1637.     configure()
  1638.     (cmd, local_opts) = parseArgs(sys.argv[1:])
  1639.     if cmd:
  1640.         try:
  1641.             cmd.perform(local_opts)
  1642.         except KeyError, e:
  1643.             if e and string.find(e[0], "Specific key requires an operator") >= 0:
  1644.                 print_error("Invalid syntax: missing operator")
  1645.                 print_error("If you want only specific versions please use one of")
  1646.                 print_error("the following operators as prefix for the package name:")
  1647.                 print_error("   >  >=  =  <=  <")
  1648.                 print_error("Example to only match gcc versions greater or equal 3.2:")
  1649.                 print_error("   >=sys-devel/gcc-3.2")
  1650.                 print_error("")
  1651.                 print_error("Note: The symbols > and < are used for redirection in the shell")
  1652.                 print_error("and must be quoted if either one is used.")
  1653.                 
  1654.             else:
  1655.                 print_error("Internal portage error, terminating")
  1656.                 if len(e[0]):
  1657.                     print_error(str(e))
  1658.             sys.exit(2)
  1659.         except ValueError, e:
  1660.             if e and type(e[0]) == types.ListType:
  1661.                 print_error("Ambiguous package name " + pp.emph("\"" + local_opts[0] + "\""))
  1662.                 print_error("Please use one of the following long names:")
  1663.                 for p in e[0]:
  1664.                     print_error("    " + str(p))
  1665.             else:
  1666.                 print_error("Internal portage error, terminating")
  1667.                 if len(e[0]):
  1668.                     print_error(str(e[0]))
  1669.             sys.exit(2)
  1670.         except KeyboardInterrupt:
  1671.             print_info(0, "Interrupted by user, aborting.")
  1672.     else:
  1673.         print_error("No command or unknown command given")
  1674.         printUsage()
  1675.   
  1676.